package com.hedl.practice.diyJava.encryptionandsecurity.coding_algorithm.test;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;

public class Main {
    public static void main(String[] args) {
        /**
         * 注意：URL编码是编码算法，不是加密算法。
         * URL编码的目的是把任意文本数据编码为%前缀表示的文本，编码后的文本仅包含A~Z，a~z，0~9，-，_，.，*和%，便于浏览器和服务器处理
         */
        String encode = URLEncoder.encode("中文", StandardCharsets.UTF_8);
        System.out.println("编码结果："+encode);
        System.out.println("解码："+URLDecoder.decode(encode,StandardCharsets.UTF_8));

        System.out.println("==============================Base64编码=======================");
        /**
         * Base64编码可以把任意长度的二进制数据变为纯文本，且只包含A~Z、a~z、0~9、+、/、=这些字符。
         * 它的原理是把3字节的二进制数据按6bit一组，用4个int整数表示，然后查表，把int整数用索引对应到字符，得到编码后的字符串。
         *
         * 举个例子：3个byte数据分别是e4、b8、ad，按6bit分组得到39、0b、22和2d：
         *
         * ┌───────────────┬───────────────┬───────────────┐
         * │      e4       │      b8       │      ad       │
         * └───────────────┴───────────────┴───────────────┘
         * ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
         * │1│1│1│0│0│1│0│0│1│0│1│1│1│0│0│0│1│0│1│0│1│1│0│1│
         * └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
         * ┌───────────┬───────────┬───────────┬───────────┐
         * │    39     │    0b     │    22     │    2d     │
         * └───────────┴───────────┴───────────┴───────────┘
         * 因为6位整数的范围总是0~63，所以，能用64个字符表示：字符A~Z对应索引0~25，字符a~z对应索引26~51，字符0~9对应索引52~61，最后两个索引62、63分别用字符+和/表示。
         */
        byte[] b = new byte[]{(byte) 0xe4, (byte) 0xb8, (byte) 0xad};
        String s = Base64.getEncoder().encodeToString(b);
        System.out.println("Base64编码："+s);
        byte[] decode = Base64.getDecoder().decode(s);
        System.out.println("Base64解码："+Arrays.toString(decode));

        /**
         * 如果输入的byte[]数组长度不是3的整数倍肿么办？
         *      这种情况下，需要对输入的末尾补一个或两个0x00，编码后，在结尾加一个=表示补充了1个0x00，加两个=表示补充了2个0x00，
         *      解码的时候，去掉末尾补充的一个或两个0x00即可。
         *
         * 实际上，因为编码后的长度加上=总是4的倍数，所以即使不加=也可以计算出原始输入的byte[]。
         *      Base64编码的时候可以用withoutPadding()去掉=，解码出来的结果是一样的：
         */
        byte[] input = new byte[]{ (byte) 0xe4, (byte) 0xb8, (byte) 0xad, 0x21 };
        String b64encoded = Base64.getEncoder().encodeToString(input);
        String b64encoded1 = Base64.getEncoder().withoutPadding().encodeToString(input);
        System.out.println("Base64编码："+b64encoded);
        System.out.println("Base64编码"+b64encoded1);
        byte[] decode1 = Base64.getDecoder().decode(b64encoded);
        System.out.println("Base64解码："+Arrays.toString(decode1));


        System.out.println("=====================================================");
        /**
         * 因为标准的Base64编码会出现+、/和=，所以不适合把Base64编码后的字符串放到URL中。
         * 一种针对URL的Base64编码可以在URL中使用的Base64编码，它仅仅是把+变成-，/变成_：
         */
        byte[] input1 = new byte[]{0x01, 0x02, 0x7f, 0x00};
        String b64encoded2 = Base64.getUrlEncoder().encodeToString(input1);
        System.out.println("Base64编码："+b64encoded2);
        byte[] decode2 = Base64.getUrlDecoder().decode(b64encoded2);
        System.out.println("Base64解码："+Arrays.toString(decode2));
        /**
         * Base64编码的目的是把二进制数据变成文本格式，这样在很多文本中就可以处理二进制数据。例如，电子邮件协议就是文本协议，如果要在电子邮件中添加一个二进制文件，就可以用Base64编码，然后以文本的形式传送。
         * Base64编码的缺点是传输效率会降低，因为它把原始数据的长度增加了1/3。
         * 和URL编码一样，Base64编码是一种编码算法，不是加密算法。
         * 如果把Base64的64个字符编码表换成32个、48个或者58个，就可以使用Base32编码，Base48编码和Base58编码。字符越少，编码的效率就会越低。
         */
    }
}

/**
 * 小结：
 *      URL编码和Base64编码都是编码算法，它们不是加密算法；
 *      URL编码的目的是把任意文本数据编码为%前缀表示的文本，便于浏览器和服务器处理；
 *      Base64编码的目的是把任意二进制数据编码为文本，但编码后数据量会增加1/3。
 */
